Applied patch from Jan Heller that ports BablFishPath class to the new
authorØyvind Kolås <ok@src.gnome.org>
Thu, 3 Apr 2008 00:47:05 +0000 (00:47 +0000)
committerØyvind Kolås <ok@src.gnome.org>
Thu, 3 Apr 2008 00:47:05 +0000 (00:47 +0000)
list API and the list API is a bit expanded. Further, the algorithm
for generating the shortest conversion path is reformulated to be more
readable and comprehensible and thoroughly commented. The algorithm
for processing the conversion paths is reformulated and commented.
Also contains minor readability cleanups and speedups.
* babl/babl-classes.h:
* babl/babl-conversion.c:
* babl/babl-db.c:
* babl/babl-fish-path.c:
* babl/babl-fish-stats.c:
* babl/babl-fish.c:
* babl/babl-list.c:
* babl/babl-list.h:
* tests/babl_fish_path_fitness.c:

svn path=/trunk/; revision=301

ChangeLog
babl/babl-classes.h
babl/babl-conversion.c
babl/babl-db.c
babl/babl-fish-path.c
babl/babl-fish-stats.c
babl/babl-fish.c
babl/babl-list.c
babl/babl-list.h
tests/babl_fish_path_fitness.c

index e3f3ade1d74b601ceec1b07d44c7fb506f9115a3..c278ccb780e48e42411d664b85057530644b916b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2008-04-03  Øyvind Kolås  <pippin@gimp.org>
+
+       Applied patch from Jan Heller that ports BablFishPath class to the new
+       list API and the list API is a bit expanded. Further, the algorithm
+       for generating the shortest conversion path is reformulated to be more
+       readable and comprehensible and thoroughly commented. The algorithm
+       for processing the conversion paths is reformulated and commented. 
+       Also contains minor readability cleanups and speedups.
+
+       * babl/babl-classes.h:
+       * babl/babl-conversion.c:
+       * babl/babl-db.c:
+       * babl/babl-fish-path.c: 
+       * babl/babl-fish-stats.c:
+       * babl/babl-fish.c:
+       * babl/babl-list.c:
+       * babl/babl-list.h:
+       * tests/babl_fish_path_fitness.c:
+
 2008-04-01  Øyvind Kolås  <pippin@gimp.org>
 
        * babl/babl-classes.h:
index 8544d4e789aea6f7984cf686bcd20b44f5170604..25c5517be41851e6ad927438653352cfe849eea2 100644 (file)
@@ -177,6 +177,8 @@ typedef struct
   int              planar;
   double           loss; /*< average relative error when converting
                              from and to RGBA double */
+  int              visited; /* for convenience in code while searching 
+                               for conversion paths */
 } BablFormat;
 
 typedef struct
@@ -241,9 +243,7 @@ typedef struct
   BablFish         fish;
   double           cost;   /* number of  ticks *10 + chain_length */
   double           loss;   /* error introduced */
-
-  int              conversions;
-  BablConversion  *conversion[BABL_HARD_MAX_PATH_LENGTH];
+  BablList         *conversion_list;
 } BablFishPath;
 
 /* BablFishReference
index 3dd3f1124fb6334913172f2531b9a7a3cb0c2f9d..461eee34ac1e2d23a64856472183d408fb0e70e8 100644 (file)
@@ -277,7 +277,7 @@ babl_conversion_new (void *first_arg,
   babl_db_insert (db, babl);
   if (!source->type.from_list)
     source->type.from_list = babl_list_init_with_size (BABL_CONVERSIONS);
-  babl_list_insert (source->type.from_list, babl);  
+  babl_list_insert_last (source->type.from_list, babl);  
   return babl;
 }
 
@@ -474,6 +474,11 @@ babl_conversion_error (BablConversion *conversion)
   if (!conversion)
     return 0.0;
 
+  if (conversion->error != -1.0)  /* double conversion against a set value should work */
+    {
+      return conversion->error;
+    }
+
   fmt_source      = BABL (conversion->source);
   fmt_destination = BABL (conversion->destination);
 
@@ -496,10 +501,6 @@ babl_conversion_error (BablConversion *conversion)
     {
       conversion->error = 0.000042;
     }
-  if (conversion->error != -1.0)  /* double conversion against a set value should work */
-    {
-      return conversion->error;
-    }
 
   test = test_create ();
 
index c5a44804618e333491ab07b3670f2a28e644e495..88f51823abb23991162d6d7186ee52c5feed5a9c 100644 (file)
@@ -97,7 +97,7 @@ babl_db_insert (BablDb *db,
   if (item->instance.id)
     babl_hash_table_insert (db->id_hash, item);
   babl_hash_table_insert (db->name_hash, item);
-  babl_list_insert (db->babl_list, item);
+  babl_list_insert_last (db->babl_list, item);
 
   /* this point all registered items pass through, a nice
   * place to brand them with where the item came from. */
index 6534167b45684d0e6f4167a3e348991a6559808d..a0a85829f61b94590aa1a580ac06e18529fe83a7 100644 (file)
 #include <math.h>
 #include "babl-internal.h"
 
+#define BABL_LEGAL_ERROR    0.000001
+#define BABL_MAX_COST_VALUE 2000000
+
 static double
-chain_error (const Babl      *fmt_source,
-             const Babl      *fmt_destination,
-             BablConversion **chain,
-             int              conversions);
+get_conversion_path_error (BablList *path);
+
+static long
+process_conversion_path (BablList *path,
+                         void     *source_buffer,
+                         void     *destination_buffer,
+                         long     n);
+
+static void
+get_conversion_path (Babl *current_format,
+                     int current_length, 
+                     int max_length);
+
+static double *
+test_create (void);
+
+static char *
+create_name (const Babl *source,
+             const Babl *destination,
+             int         is_reference);
+
+static double legal_error (void);
+
+static int max_path_length (void);
 
-//#define BABL_LEGAL_ERROR 0.000001
-//#define BABL_LEGAL_ERROR 0.01
 
 static double legal_error (void)
 {
@@ -40,7 +61,7 @@ static double legal_error (void)
   if (env)
     error = atof (env);
   else
-    error = 0.000001;
+    error = BABL_LEGAL_ERROR;
   return error;
 }
 
@@ -64,207 +85,99 @@ static int max_path_length (void)
   return max_length;
 }
 
-typedef struct BablChainContext
-{
-  const Babl *from;
-  const Babl *to;
-
-  double *best_cost;
-  double *best_loss;
-  double *best_error;
-
-  BablConversion **chain;
-  int             *conversions;
-
-  BablConversion **temp_chain;
-  int              temp_conversions;
-
-  int max_conversions;
-} BablChainContext;
-
-static int
-chain_gen_each (Babl *babl,
-                void *userdata);
-
-static int
-get_conversion_chain (const Babl      *from,
-                      const Babl      *to,
-                      double          *best_cost,
-                      double          *best_loss,
-                      double          *best_error,
-                      BablConversion **chain,
-                      int             *conversions,
-                      BablConversion **temp_chain,
-                      int              temp_conversions,
-                      int              max_conversions)
-{
-  BablChainContext context;
 
-  if (temp_conversions >= max_conversions)
-    return 0;
+/* The task of BablFishPath construction is to compute
+ * the shortest path in a graph where formats are the vertices
+ * and conversions are the edges. However, there is an additional
+ * constraint to the shortest path, that limits conversion error 
+ * introduced by such a path to be less than BABL_ERROR. This
+ * prohibits usage of any reasonable shortest path construction 
+ * algorithm such as Dijkstra's algorithm. The shortest path is
+ * constructed by enumerating all available paths that are less 
+ * than BABL_PATH_LENGTH long, computing their costs and 
+ * conversion errors and backtracking. The backtracking is 
+ * implemented by recursive function get_conversion_path ().
+ */
 
-  if (temp_conversions == 0)
-    {
-      /* chain initialization */
-      *conversions  = 0;
-      *best_cost    = 200000.0;
-      *best_loss    = 200000.0;
-      *best_error   = 200000.0;
-      chain[0]      = NULL;
-      temp_chain[0] = NULL;
-
-      /* Bail out if requesting something stupid (to and from same format, an
-       * optimized memcpy should be used instead (assuming linear buffers).
-       */
+static Babl *fish_path;
+static Babl *to_format;
+static BablList *current_path;
 
-      if (from == to)
-        return 0;
+static void
+get_conversion_path (Babl *current_format,
+                     int current_length, 
+                     int max_length)
+{
+  if (current_length > max_length) 
+    {
+      /* We have reached the maximum recursion
+       * depth, let's bail out */
+      return;
     }
+  else if ((current_length > 0) && (current_format == to_format))
+    {
+       /* We have found a candidate path, let's 
+        * see about it's properties */
+      double temp_cost  = 0.0;
+      double temp_error = 1.0;
+      int    i;
 
-  /* copy parameters to stack */
-  context.from = from;
-  context.to   = to;
-
-  context.best_cost   = best_cost;
-  context.best_loss   = best_loss;
-  context.best_error  = best_error;
-  context.chain       = chain;
-  context.conversions = conversions;
-
-  context.temp_chain       = temp_chain;
-  context.temp_conversions = temp_conversions;
-
-  context.max_conversions = max_conversions;
+      for (i = 0; i < babl_list_size (current_path); i++)
+        {
+          temp_error *= (1.0 + babl_conversion_error ((BablConversion *) current_path->items[i]));
+          temp_cost  += babl_conversion_cost ((BablConversion *) current_path->items[i]);
+        }
 
-  if (temp_conversions == 0)
-    {
-      temp_chain[temp_conversions] = NULL;
-      babl_assert (from);
-      babl_assert (from->class_type == BABL_FORMAT);
-      if (!from->format.from_list)
-        return 0;
-
-      babl_list_each (from->format.from_list,
-                      chain_gen_each,
-                      &context);
+      if (temp_cost < fish_path->fish_path.cost &&
+          temp_error - 1.0 <= legal_error () &&     /* check this before the next; 
+                                                       which does a more accurate
+                                                       measurement of the error */
+          (temp_error = get_conversion_path_error (current_path)) <= legal_error ()
+      )
+        {
+          /* We have found the best path so far,
+           * let's copy it into our new fish */
+          fish_path->fish_path.cost = temp_cost;
+          fish_path->fish.error  = temp_error;
+          babl_list_copy (current_path, fish_path->fish_path.conversion_list);
+        }
     }
-  else
+  else 
     {
-      if (BABL (temp_chain[temp_conversions - 1]) &&
-          BABL (temp_chain[temp_conversions - 1]->destination)->
-          format.from_list)
-
-        babl_list_each (
-          BABL (temp_chain[temp_conversions - 1]->destination)->
-          format.from_list,
-          chain_gen_each,
-          &context);
-    }
+      /* 
+       * Bummer, we have to search deeper... */
+      BablList *list;
+      int i;
 
-  return 0;
-}
+      list = current_format->format.from_list;
+      if (list)
+        {
+          /* Mark the current format in conversion path as visited */
+          current_format->format.visited = 1;
 
-static int
-chain_contains_fmt (BablConversion **chain,
-                    int              conversions,
-                    const Babl      *fmt)
-{
-  int i;
+           /* Iterate through unvisited formats from the current format ...*/
+          for (i = 0; i < babl_list_size (list); i++)
+            {
+              Babl *next_conversion = BABL (list->items[i]);
+              Babl *next_format = BABL (next_conversion->conversion.destination);
+              if (!next_format->format.visited)
+                {
+                  /* next_format is not in the current path, we can pay a visit */
+                   babl_list_insert_last (current_path, next_conversion);
+                   get_conversion_path (next_format, current_length + 1, max_length);
+                   babl_list_remove_last (current_path);
+                 }
 
-  for (i = 0; i < conversions; i++)
-    if (BABL (chain[i]->destination) == fmt ||
-        BABL (chain[i]->source) == fmt)
-      {
-        return 1;
-      }
-  return 0;
-}
+            }
 
-static int
-chain_gen_each (Babl *babl,
-                void *userdata)
-{
-  BablChainContext *c = userdata;
-
-  /* fill in the conversion for the chain index we are at */
-  c->temp_chain[c->temp_conversions] = (BablConversion *) babl;
-
-  {
-    if ((BABL (babl->conversion.destination) == c->to))
-      {
-        /* a candidate path has been found */
-
-        double temp_cost  = 0.0;
-        double temp_error = 1.0;
-        int    i;
-
-        for (i = 0; i < c->temp_conversions + 1; i++)
-          {
-            temp_error *= (1.0 + babl_conversion_error (c->temp_chain[i]));
-            temp_cost  += babl_conversion_cost (c->temp_chain[i]);
-          }
-
-        if (temp_cost < *c->best_cost &&
-            temp_error - 1.0 <= legal_error () &&     /* this check before the next; which does a more accurate
-                                                         measurement of the error */
-            (temp_error = chain_error (c->from, c->to, c->temp_chain, c->temp_conversions + 1)) <= legal_error ()
-        )
-          {
-            int i;
-
-            *c->best_cost   = temp_cost;
-            *c->best_error  = temp_error;
-            *c->conversions = c->temp_conversions + 1;
-
-            /* copy from temp chain to best chain */
-            for (i = 0; i < *c->conversions; i++)
-              c->chain[i] = c->temp_chain[i];
-          }
-      }
-    else if (babl->conversion.source != babl->conversion.destination &&
-             !chain_contains_fmt (c->temp_chain,
-                                  c->temp_conversions,
-                                  BABL (babl->conversion.destination)))
-      {
-        /* try to add another conversion level in chain,.. */
-        get_conversion_chain (c->from,      /* irrelevant when recalled */
-                              c->to,
-
-                              c->best_cost,
-                              c->best_loss,
-                              c->best_error,
-                              c->chain,
-                              c->conversions,
-
-                              c->temp_chain,
-                              c->temp_conversions + 1,
-
-                              c->max_conversions);
+          /* Remove the current format from current path */
+          current_format->format.visited = 0;  
+        }
       }
-  }
-  return 0;
-}
-
-static inline Babl *
-assert_conversion_find (void *source,
-                        void *destination)
-{
-  int      i = 0;
-  BablList *conversion_list;
-  Babl     *conversion;
-
-  conversion_list = BABL (source)->type.from_list;
-  for (i = 0; i < babl_list_size (conversion_list); i++)
-    {
-      conversion = BABL (conversion_list->items[i]);
-      if (conversion->conversion.destination == destination)
-        return conversion;
-    }
-  babl_fatal ("failed, aborting");
-  return NULL;
 }
 
 static char buf[1024];
+
 static char *
 create_name (const Babl *source,
              const Babl *destination,
@@ -282,7 +195,6 @@ babl_fish_path (const Babl *source,
 {
   Babl           *babl = NULL;
   char           *name = create_name (source, destination, 1);
-  BablConversion *temp_chain[BABL_HARD_MAX_PATH_LENGTH];
 
   babl = babl_db_exist_by_name (babl_fish_db (), name);
   if (babl) 
@@ -301,39 +213,32 @@ babl_fish_path (const Babl *source,
 
   babl = babl_calloc (1, sizeof (BablFishPath) +
                       strlen (name) + 1);
-  babl->class_type    = BABL_FISH_PATH;
-  babl->instance.id   = 0;
-  babl->instance.name = ((char *) babl) + sizeof (BablFishPath);
+
+  babl->class_type                = BABL_FISH_PATH;
+  babl->instance.id               = 0;
+  babl->instance.name             = ((char *) babl) + sizeof (BablFishPath);
   strcpy (babl->instance.name, name);
-  babl->fish.source      = source;
-  babl->fish.destination = destination;
+  babl->fish.source               = source;
+  babl->fish.destination          = destination;
+  babl->fish.processings          = 0;
+  babl->fish.pixels               = 0;
+  babl->fish.usecs                = 0;
+  babl->fish.error                = BABL_MAX_COST_VALUE;
+  babl->fish_path.cost            = BABL_MAX_COST_VALUE;
+  babl->fish_path.loss            = BABL_MAX_COST_VALUE;
+  babl->fish_path.conversion_list = babl_list_init_with_size (BABL_HARD_MAX_PATH_LENGTH);
 
-  babl->fish.processings = 0;
-  babl->fish.pixels      = 0;
-  babl->fish.usecs       = 0;
-  babl->fish.error       = 200000;
+  current_path = babl_list_init_with_size (BABL_HARD_MAX_PATH_LENGTH); 
+  fish_path = babl;
+  to_format = (Babl *) destination;
 
-  babl->fish_path.cost          = 200000;
-  babl->fish_path.loss          = 200000;
-  babl->fish_path.conversions   = 0;
-  babl->fish_path.conversion[0] = NULL;
+  get_conversion_path ((Babl *) source, 0, max_path_length ());
 
-  babl_assert (source->class_type == BABL_FORMAT);
-  babl_assert (destination->class_type == BABL_FORMAT);
+  babl_list_destroy (current_path);
 
-  get_conversion_chain (source,
-                        destination,
-                        &babl->fish_path.cost,
-                        &babl->fish_path.loss,
-                        &babl->fish.error,
-                        (BablConversion **) (babl->fish_path.conversion),
-                        &babl->fish_path.conversions,
-                        temp_chain,
-                        0,
-                        max_path_length ());
-
-  if (babl->fish_path.conversions == 0)
+  if (babl_list_size (babl->fish_path.conversion_list) == 0)
     {
+      babl_list_destroy (babl->fish_path.conversion_list);
       babl_free (babl);
       return NULL;
     }
@@ -345,72 +250,6 @@ babl_fish_path (const Babl *source,
   return babl;
 }
 
-static long
-chain_process (BablConversion *chain[],
-               int             conversions,
-               void           *source,
-               void           *destination,
-               long            n)
-{
-  void *bufA = NULL;
-  void *bufB = NULL;
-  int   i;
-
-  babl_assert (source);
-  babl_assert (destination);
-
-  if (conversions > 1)
-    bufA = babl_malloc (n * sizeof (double) * 5);
-  if (conversions > 2)
-    bufB = babl_malloc (n * sizeof (double) * 5);
-
-  for (i = 0; i < conversions; i++)
-    {
-      if (i == 0 && conversions == 1)
-        {
-          babl_conversion_process (BABL (chain[i]),
-                                   source, destination, n);
-        }
-      else if (i == 0)
-        {
-          babl_conversion_process (BABL (chain[i]),
-                                   source, bufA, n);
-        }
-      else if (i % 2 == 0)
-        {
-          if (i + 1 == conversions)
-            {
-              babl_conversion_process (BABL (chain[i]),
-                                       bufB, destination, n);
-            }
-          else
-            {
-              babl_conversion_process (BABL (chain[i]),
-                                       bufB, bufA, n);
-            }
-        }
-      else if (i % 2 == 1)
-        {
-          if (i + 1 == conversions)
-            {
-              babl_conversion_process (BABL (chain[i]),
-                                       bufA, destination, n);
-            }
-          else
-            {
-              babl_conversion_process (BABL (chain[i]),
-                                       bufA, bufB, n);
-            }
-        }
-    }
-  if (bufA)
-    babl_free (bufA);
-  if (bufB)
-    babl_free (bufB);
-
-  return n;
-}
-
 long
 babl_fish_path_process (Babl *babl,
                         void *source,
@@ -422,27 +261,94 @@ babl_fish_path_process (Babl *babl,
   babl_assert (source);
   babl_assert (destination);
 
-  ret = chain_process (babl->fish_path.conversion,
-                       babl->fish_path.conversions,
-                       source,
-                       destination,
-                       n);
+  ret = process_conversion_path (babl->fish_path.conversion_list,
+                                 source,
+                                 destination,
+                                 n);
 
   return ret;
 }
 
+static long
+process_conversion_path (BablList *path,
+                         void     *source_buffer,
+                         void     *destination_buffer,
+                         long     n)
+{
+  int conversions = babl_list_size (path);
 
-#define num_test_pixels  (128 + 16 + 16)
+  if (conversions == 1)
+    {
+      babl_conversion_process (BABL (babl_list_get_first (path)),
+                               source_buffer,
+                               destination_buffer,
+                               n);
+    }
+  else 
+    {
+      void *aux1_buffer = babl_malloc (n * sizeof (double) * 5);
+      void *aux2_buffer = NULL;
+      void *swap_buffer = NULL;
+      int   i;
+
+      if (conversions > 2) 
+        {
+          /* We'll need one more auxiliary buffer */
+          aux2_buffer = babl_malloc (n * sizeof (double) * 5);
+        }
+
+      /* The first conversion goes from source_buffer to aux1_buffer */
+      babl_conversion_process (babl_list_get_first (path),
+                               source_buffer,
+                               aux1_buffer,
+                               n);
+
+      /* Process, if any, conversions between the first and the last 
+       * conversion in the path, in a loop */
+      for (i = 1; i < conversions - 1; i++)
+        {
+          babl_conversion_process (path->items[i],
+                                   aux1_buffer,
+                                   aux2_buffer,
+                                   n);
+          /* Swap the auxiliary buffers */
+          swap_buffer = aux1_buffer;
+          aux1_buffer = aux2_buffer;
+          aux2_buffer = swap_buffer;
+        }
+
+      /* The last conversion goes from aux1_buffer to destination_buffer */
+      babl_conversion_process (babl_list_get_last (path),
+                               aux1_buffer,
+                               destination_buffer,
+                               n);
+
+      /* Free auxiliary buffers */
+      if (aux1_buffer)
+        babl_free (aux1_buffer);
+      if (aux2_buffer)
+        babl_free (aux2_buffer);
+  }
+
+  return n;
+}
+
+#define NUM_TEST_PIXELS  (128 + 16 + 16)
 
 static double *
 test_create (void)
 {
-  double *test;
-  int     i, j;
+  static double test[sizeof (double) * NUM_TEST_PIXELS * 4];
+  static int    flag = 0; 
+  int           i, j;
 
-  srandom (20050728);
+  /* There is no need to generate the test
+   * more times ... */
+  if (flag)
+    return test;
+  flag = 1;  
 
-  test = babl_malloc (sizeof (double) * num_test_pixels * 4);
+  srandom (20050728);
 
   /*  add 128 pixels in the valid range between 0.0 and 1.0  */
   for (i = 0; i < 128 * 4; i++)
@@ -460,10 +366,7 @@ test_create (void)
 }
 
 static double
-chain_error (const Babl      *fmt_source,
-             const Babl      *fmt_destination,
-             BablConversion **chain,
-             int              conversions)
+get_conversion_path_error (BablList *path)
 {
   Babl *fmt_rgba_double = babl_format_new (
     babl_model ("RGBA"),
@@ -476,6 +379,9 @@ chain_error (const Babl      *fmt_source,
 
   double  error = 0.0;
 
+  Babl   *fmt_source = (Babl *) BABL (babl_list_get_first (path))->conversion.source;
+  Babl   *fmt_destination = (Babl *) BABL (babl_list_get_last (path))->conversion.destination;
+
   double *test;
   void   *source;
   void   *destination;
@@ -492,54 +398,53 @@ chain_error (const Babl      *fmt_source,
 
   test = test_create ();
 
-  source                      = babl_calloc (num_test_pixels,
+  source                      = babl_calloc (NUM_TEST_PIXELS,
                                              fmt_source->format.bytes_per_pixel);
-  destination                 = babl_calloc (num_test_pixels,
+  destination                 = babl_calloc (NUM_TEST_PIXELS,
                                              fmt_destination->format.bytes_per_pixel);
-  ref_destination             = babl_calloc (num_test_pixels,
+  ref_destination             = babl_calloc (NUM_TEST_PIXELS,
                                              fmt_destination->format.bytes_per_pixel);
-  destination_rgba_double     = babl_calloc (num_test_pixels,
+  destination_rgba_double     = babl_calloc (NUM_TEST_PIXELS,
                                              fmt_rgba_double->format.bytes_per_pixel);
-  ref_destination_rgba_double = babl_calloc (num_test_pixels,
+  ref_destination_rgba_double = babl_calloc (NUM_TEST_PIXELS,
                                              fmt_rgba_double->format.bytes_per_pixel);
 
   /* create sourcebuffer from testbuffer in the correct format */
   babl_process (fish_rgba_to_source,
-                test, source, num_test_pixels);
+                test, source, NUM_TEST_PIXELS);
 
   /* calculate the reference buffer of how it should be */
   babl_process (fish_reference,
-                source, ref_destination, num_test_pixels);
+                source, ref_destination, NUM_TEST_PIXELS);
 
-  /* calculate this chains view of what the result should be */
-  chain_process (chain, conversions, source, destination, num_test_pixels);
+  /* calculate this path's view of what the result should be */
+  process_conversion_path (path, source, destination, NUM_TEST_PIXELS);
 
   /* transform the reference and the actual destination buffers to RGBA
    * for comparison with each other
    */
   babl_process (fish_destination_to_rgba,
-                ref_destination, ref_destination_rgba_double, num_test_pixels);
+                ref_destination, ref_destination_rgba_double, NUM_TEST_PIXELS);
   babl_process (fish_destination_to_rgba,
-                destination, destination_rgba_double, num_test_pixels);
+                destination, destination_rgba_double, NUM_TEST_PIXELS);
 
   error = babl_rel_avg_error (destination_rgba_double,
                               ref_destination_rgba_double,
-                              num_test_pixels * 4);
+                              NUM_TEST_PIXELS * 4);
 
   fish_rgba_to_source->fish.processings--;
   fish_reference->fish.processings--;
   fish_destination_to_rgba->fish.processings -= 2;
 
-  fish_rgba_to_source->fish.pixels      -= num_test_pixels;
-  fish_reference->fish.pixels           -= num_test_pixels;
-  fish_destination_to_rgba->fish.pixels -= 2 * num_test_pixels;
+  fish_rgba_to_source->fish.pixels      -= NUM_TEST_PIXELS;
+  fish_reference->fish.pixels           -= NUM_TEST_PIXELS;
+  fish_destination_to_rgba->fish.pixels -= 2 * NUM_TEST_PIXELS;
 
   babl_free (source);
   babl_free (destination);
   babl_free (destination_rgba_double);
   babl_free (ref_destination);
   babl_free (ref_destination_rgba_double);
-  babl_free (test);
 
   return error;
 }
index 77087d3838f0a789f1e08479b368e3184ebcad3a..52daf991df4c91a7e70e74650f8991b046996bde 100644 (file)
@@ -48,7 +48,7 @@ table_destination_each (Babl *babl,
 
             fprintf (output_file, "<td class='cell'%s><a href='javascript:o()'>%s",
                      fish->fish.processings > 0 ? " style='background-color: #69f'" : "",
-                     utf8_bar[fish->fish_path.conversions]);
+                     utf8_bar[babl_list_size (fish->fish_path.conversion_list)]);
 
             {
               int i;
@@ -68,12 +68,14 @@ table_destination_each (Babl *babl,
               fprintf (output_file, "<td style='text-align:right'><em>error</em></td>");
               fprintf (output_file, "</tr>");
 
-              for (i = 0; i < fish->fish_path.conversions; i++)
+              for (i = 0; i < babl_list_size (fish->fish_path.conversion_list); i++)
                 {
                   fprintf (output_file, "<tr>");
-                  fprintf (output_file, "<td>%s</td>", BABL (fish->fish_path.conversion[i])->instance.name);
-                  fprintf (output_file, "<td class='r'>%li</td>", babl_conversion_cost (&BABL (fish->fish_path.conversion[i])->conversion));
-                  fprintf (output_file, "<td class='r'>%e</td>", babl_conversion_error (&BABL (fish->fish_path.conversion[i])->conversion));
+                  fprintf (output_file, "<td>%s</td>", BABL (fish->fish_path.conversion_list->items[i])->instance.name);
+                  fprintf (output_file, "<td class='r'>%li</td>", 
+                    babl_conversion_cost (&BABL (fish->fish_path.conversion_list->items[i])->conversion));
+                  fprintf (output_file, "<td class='r'>%e</td>", 
+                    babl_conversion_error (&BABL (fish->fish_path.conversion_list->items[i])->conversion));
                   fprintf (output_file, "</tr>");
                 }
 
index c77acaf441e38b73c314ba4d44619ddce848ae8b..7f22ebf99c9f2e322677e118ac12fee68e9125ed 100644 (file)
@@ -213,6 +213,8 @@ static int
 each_babl_fish_destroy (Babl *babl,
                         void *data)
 {
+  if (babl->class_type == BABL_FISH_PATH)
+    babl_list_destroy (babl->fish_path.conversion_list);
   babl_free (babl);
   return 0;  /* continue iterating */
 }
index 9dbd9595013ce8a6d26eea46a71838f76fadc570..1373bb9a0abaee2dc1ffecc3c9a73165745c6412 100644 (file)
@@ -63,29 +63,78 @@ int
 babl_list_size (BablList *list)
 {
     babl_assert (list);
+
     return list->count;
 }
 
-void
-babl_list_insert (BablList *list,
-                  Babl     *item)
+inline void
+babl_list_insert_last (BablList *list,
+                       Babl     *item)
 {
   babl_assert(list);
   babl_assert(BABL_IS_BABL(item));
 
   if (list->size < list->count + 1)
     {
-        Babl **new_items;
+      Babl **new_items;
 
-        new_items = babl_realloc (list->items, (list->size * 2) * sizeof (BablInstance *));
-        babl_assert (new_items);
-        list->items = new_items;
-        memset (list->items + list->size, 0, list->size * sizeof (BablInstance *));
-        list->size *= 2;
+      new_items = babl_realloc (list->items, (list->size * 2) * sizeof (BablInstance *));
+      babl_assert (new_items);
+      list->items = new_items;
+      memset (list->items + list->size, 0, list->size * sizeof (BablInstance *));
+      list->size *= 2;
     }
     list->items[list->count++] = item;
 }
 
+inline void
+babl_list_remove_last (BablList *list)
+{
+  babl_assert (list);
+  babl_assert (list->count > 0);
+
+  list->count--;
+}
+
+inline Babl *
+babl_list_get_first (BablList *list)
+{
+  babl_assert (list);
+  babl_assert (list->count > 0);
+
+  return (list->items[0]);
+}
+
+inline Babl *
+babl_list_get_last (BablList *list)
+{
+  babl_assert (list);
+  babl_assert (list->count > 0);
+
+  return (list->items[list->count - 1]);
+}
+
+inline void 
+babl_list_copy (BablList *from,
+                BablList *to)
+{
+  babl_assert (from);
+  babl_assert (to);
+
+  if (to->size < from->count)
+    {
+      Babl **new_items;
+
+      new_items = babl_realloc (to->items, from->count * sizeof (BablInstance *));
+      babl_assert (new_items);
+      to->items = new_items;
+      to->size = from->count;
+    }
+
+    memcpy (to->items, from->items, from->count * sizeof (BablInstance *));
+    to->count = from->count;
+}
+
 void
 babl_list_each (BablList         *list,
                 BablEachFunction each_fun,
index 07047cc9848d981a68e0ec48805743fcaa93462d..b543cde025fe7c818de7accfb6a9da4c80cfd466 100644 (file)
@@ -47,9 +47,22 @@ babl_list_destroy (BablList *list);
 int
 babl_list_size (BablList *list);
 
-void
-babl_list_insert (BablList *list,
-                  Babl     *item);
+inline void
+babl_list_insert_last (BablList *list,
+                       Babl     *item);
+
+inline void
+babl_list_remove_last (BablList *list);
+
+inline Babl *
+babl_list_get_first (BablList *list);
+
+inline Babl *
+babl_list_get_last (BablList *list);
+
+inline void 
+babl_list_copy (BablList *from,
+                BablList *to);
 
 void
 babl_list_each (BablList      *list,
index 5cc52063a4e45d362a5c52d838a032e05a7402e3..2f18152e2fad821d5a2e5983e86eae70ce4f4c5d 100644 (file)
@@ -43,8 +43,8 @@ static int destination_each (Babl *babl,
 
       if (temp)
         {
-          printf ("%s", utf8_bar[temp->fish_path.conversions]);
-          total_length += temp->fish_path.conversions;
+          printf ("%s", utf8_bar[babl_list_size (temp->fish_path.conversion_list)]);
+          total_length += babl_list_size (temp->fish_path.conversion_list);
           total_cost   += temp->fish_path.cost;
           ok++;
           total++;